﻿using System;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Collections.Generic;
using System.Drawing;
using System.Runtime.InteropServices;

namespace Piao
{

    /// <summary>
    /// HttpHelper class
    /// </summary>
    public class WebUtil
    {
        const string @refUrl = "https://dynamic.12306.cn/otsweb/";

        public string Post(string url, string content)
        {
            HttpStatusCode code = HttpStatusCode.NotFound;
            return Post(url, content, @refUrl, null, out code);
        }


        public string Post(string url, string content, string referer, Dictionary<string, string> headers, out HttpStatusCode code)
        {
            int tries = MAX_RETRY;

            int httpTimeout = TIMEOUT;
            code = 0;
            while (tries-- > 0)
            {
                try
                {
                    Log.Debug("{0} {1}", tries, url);
                    RemoteCertificateValidationCallback del = (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) => { return true; };
                    ServicePointManager.ServerCertificateValidationCallback = del;

                    var httpRequest = (HttpWebRequest)WebRequest.Create(new Uri(url));
                    httpRequestPost = httpRequest;
                    httpRequest.UserAgent = USERAGENT;
                    httpRequest.CookieContainer = cookie;
                    httpRequest.Referer = referer;
                    httpRequest.Headers.Add("Accept-Language", "zh-CN");
                    if (headers != null)
                    {
                        foreach (var pair in headers)
                        {
                            httpRequest.Headers.Add(pair.Key, pair.Value);
                        }
                    }

                    byte[] buff = Encoding.UTF8.GetBytes(content);
                    httpRequest.Method = "POST";
                    httpRequest.Timeout = httpTimeout;
                    httpRequest.ContentType = "application/x-www-form-urlencoded";
                    httpRequest.ContentLength = buff.Length;
                    if (Proxy != null)
                        httpRequest.Proxy = Proxy;

                    Stream reqStream = httpRequest.GetRequestStream();
                    reqStream.Write(buff, 0, buff.Length);
                    reqStream.Close();

                    HttpWebResponse res = (HttpWebResponse)httpRequest.GetResponse();
                    Stream resStream = null;
                    //string contentType = res.ContentType; //use utf8 only
                    string contentCode = res.ContentEncoding;

                    if (contentCode.Contains("gzip"))
                    {
                        resStream = new System.IO.Compression.GZipStream(
                                                    res.GetResponseStream(),
                                                    System.IO.Compression.CompressionMode.Decompress);

                    }
                    else
                        resStream = res.GetResponseStream();

                    StreamReader sr = new StreamReader(resStream, Encoding.UTF8);
                    code = res.StatusCode;
                    string fullResponse = sr.ReadToEnd();

                    SetCookiesForBrowser(url);

                    return fullResponse;

                }
                catch (Exception e)
                {
                    Failure++;
                    httpTimeout += 5;
                    Log.Error(e.Message);
                }
            }

            return string.Empty;
        }


        public string Get(string url)
        {
            return Get(url, @refUrl);
        }

        /// <summary>
        /// Get data from server
        /// </summary>
        /// <param name="url"></param>
        /// <returns>Return content</returns>
        public string Get(string url, string referer)
        {
            int tries = MAX_RETRY;
            int httpTimeout = TIMEOUT;
            while (tries-- > 0)
            {
                try
                {
                    var httpRequest = (HttpWebRequest)WebRequest.Create(new Uri(url));
                    httpRequestGet = httpRequest;
                    httpRequest.UserAgent = USERAGENT;
                    httpRequest.CookieContainer = cookie;
                    httpRequest.Referer = referer;
                    httpRequest.Headers.Add("Accept-Language", "zh-CN");
                    httpRequest.Method = "GET";
                    httpRequest.Timeout = httpTimeout;
                    if (Proxy != null)
                        httpRequest.Proxy = Proxy;

                    HttpWebResponse res = (HttpWebResponse)httpRequest.GetResponse();
                    StreamReader sr = new StreamReader(res.GetResponseStream(), Encoding.UTF8);
                    string fullresponse = sr.ReadToEnd();

                    SetCookiesForBrowser(url);
                    return fullresponse;

                }
                catch (Exception e)
                {
                    Failure++;
                    httpTimeout += 5;
                    Log.Error(e.Message);
                }
            }

            return string.Empty;
        }

        public System.Drawing.Image DownloadCaptcha(string url)
        {
            int tries = MAX_RETRY + 2;
            int httpTimeout = TIMEOUT;
            while (tries-- > 0)
            {
                try
                {

                    var httpRequest = (HttpWebRequest)WebRequest.Create(new Uri(url));
                    httpRequestGet = httpRequest;
                    httpRequest.UserAgent = USERAGENT;
                    httpRequest.CookieContainer = cookie;
                    httpRequest.Referer = refUrl;

                    httpRequest.Method = "GET";
                    httpRequest.Timeout = TIMEOUT;
                    if (Proxy != null)
                        httpRequest.Proxy = Proxy;

                    HttpWebResponse res = (HttpWebResponse)httpRequest.GetResponse();

                    var image = System.Drawing.Image.FromStream(res.GetResponseStream());

                    SetCookiesForBrowser(url);

                    return image;
                }
                catch (Exception e)
                {
                    httpTimeout += 5;
                    Log.Error(e.Message);
                }
            }

            return null;

        }


        private void SetCookiesForBrowser(string url)
        {
            var collection = this.cookie.GetCookies(new Uri(url));
            foreach (Cookie c in collection)
            {
                InternetSetCookie(url, c.Name, c.Value);
            }
        }

        public void GetCookiesForBrowser(string url)
        {
            StringBuilder cookieText = new StringBuilder(2048);
            int datasize = cookieText.Capacity;
            bool hr = InternetGetCookie(url, null, cookieText, ref datasize);

            if (hr && cookieText.Length > 0)
                cookie.SetCookies(new Uri(url), cookieText.ToString().Replace(';', ','));
        }

        public void Abort()
        {
            try
            {
                httpRequestGet.Abort();
            }
            catch (Exception exc)
            {
                Log.Debug(exc.Message);
            }

            try
            {
                httpRequestPost.Abort();
            }
            catch (Exception exc)
            {
                Log.Debug(exc.Message);
            }
        }


        [DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool InternetSetCookie(string lpszUrlName, string lbszCookieName, string lpszCookieData);

        [DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool InternetGetCookie(string lpszUrl, string lpszCookieName, StringBuilder lpszCookieData, ref int lpdwSize);

        #region Member Fields
        private HttpWebRequest httpRequestGet;
        private HttpWebRequest httpRequestPost;
        private CookieContainer cookie = new CookieContainer();
        public WebProxy Proxy = null;
        public int Failure = 0;




        public static int TIMEOUT = Configurations.Instance.HttpTimeout;


        public static int MAX_RETRY = 2;
        private const string USERAGENT = @"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.10 (KHTML, like Gecko) Chrome/23.0.1262.0 Safari/537.10 Ticket tookit";
        #endregion



    }


    public class RequestStringBuilder
    {
        private static readonly object locker = new object();
        Dictionary<string, string> internalDictionary = new Dictionary<string, string>();

        /// <summary>
        /// duplicated key is not allowed, but no error with be thrown.
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public string this[string key]
        {
            get
            {
                lock (locker)
                {
                    if (internalDictionary.ContainsKey(key))
                        return internalDictionary[key];
                    else
                        return string.Empty;
                }
            }
            set
            {
                lock (locker)
                {
                    if (internalDictionary.ContainsKey(key))
                        internalDictionary[key] = value;
                    else
                        internalDictionary.Add(key, value);
                }
            }
        }

        public string BuildString()
        {
            StringBuilder sb = new StringBuilder();
            foreach (var pair in internalDictionary)
            {
                sb.AppendFormat("{0}={1}&", pair.Key, pair.Value);
            }


            return sb.ToString().Trim('&');
        }

        public void Clear()
        {
            internalDictionary.Clear();
        }

        public override string ToString()
        {
            return BuildString();
        }

    }








}